// 2-1 c++多态-实例

/**
 * 面向对象的编程语言有封装、继承 、抽象、多态等4个主要的特征
 * 
 * C++ 多态
 * 多态按字面的意思就是多种形态。当类之间存在层次结构，并且类之间是通过继承关联时，就会用到多态。
 * 
 * C++ 多态意味着调用成员函数时，会根据调用函数的对象的类型来执行不同的函数。
 * 
 * 下面的实例中，基类 Shape 被派生为两个类，如下所示（示例一）：
 * 
 * 当上面的代码（示例一 #1）被编译和执行时，它会产生下列结果：
      
      parent class area : 0
      parent class area : 0

 * 导致错误输出的原因是，调用函数 printArea() 被编译器设置为基类中的版本，这就是所谓的静态多态，或静态链接 - 函数调用在程序执行前就准备好了。有时候这也被称为早绑定，因为 printArea() 函数在程序编译期间就已经设置好了。
 *
 * 但现在，让我们对程序稍作修改，在 Shape 类中，printArea() 的声明前放置关键字 virtual，如下所示（示例二 #2）：
 * 
 * 修改后，当编译和执行前面的实例代码时，它会产生以下结果：

      Rectangle class area : 12
      Triangle class area : 10
 
 * 此时，编译器看的是指针的内容，而不是它的类型。因此，由于 tri 和 rec 类的对象的地址存储在 *shape 中，所以会调用各自的 area() 函数。
 * 正如您所看到的，每个子类都有一个函数 area() 的独立实现。这就是多态的一般使用方式。有了多态，您可以有多个不同的类，都带有同一个名称但具有不同实现的函数，函数的参数甚至可以是相同的。
 *
 * ?? 多态使用受限于指针？或者说只能通过基类指针体现类的多态性吗？？
 * 指针和引用
*/

#include <iostream>
#define __mark 3

/**
 * #1 示例一
*/
#if __mark == 1
class Shape
{
protected:
  int width;
  int height;

public:
  Shape(int a, int b) : width(a), height(b)
  {
  }
  void printArea()
  {
    std::cout << "parent class area : " << 0 << std::endl;
  }
};

class Rectangle : public Shape
{
public:
  // 调用Shape的构造函数
  Rectangle(int a, int b) : Shape(a, b) {}

public:
  void printArea()
  {
    std::cout << "Rectangle class area : " << (width * height) << std::endl;
  }
};

class Triangle : public Shape
{
public:
  Triangle(int a, int b) : Shape(a, b)
  {
  }
  void printArea()
  {
    std::cout << "Triangle class area : " << (double)(width * height) / 2 << std::endl;
  }
};

int main(void)
{
  Shape *sp;
  Rectangle r1(3, 4);
  Triangle t1(4, 5);
  sp = &r1;
  sp->printArea();

  sp = &t1;
  sp->printArea();
  printf("-----------------end--------------\n");
  return 0;
}

/**
 * #2 示例二
*/
#elif __mark == 2

class Shape
{
protected:
  int width;
  int height;

public:
  Shape(int a, int b) : width(a), height(b)
  {
  }
  virtual void printArea()
  {
    std::cout << "parent class area : " << 0 << std::endl;
  }
};

class Rectangle : public Shape
{
public:
  // 调用Shape的构造函数
  Rectangle(int a, int b) : Shape(a, b) {}

public:
  void printArea()
  {
    std::cout << "Rectangle class area : " << (width * height) << std::endl;
  }
};

class Triangle : public Shape
{
public:
  Triangle(int a, int b) : Shape(a, b)
  {
  }
  void printArea()
  {
    std::cout << "Triangle class area : " << (double)(width * height) / 2 << std::endl;
  }
};

int main(void)
{
  Shape *sp;
  Rectangle r1(3, 4);
  Triangle t1(4, 5);
  // r1.printArea();
  // t1.printArea();
  sp = &r1;
  sp->printArea();

  sp = &t1;
  sp->printArea();
  printf("-----------------end--------------\n");
  return 0;
}

/**
 * #3 基类引用调用虚函数
 * C++多态意味着调用成员函数时，会根据调用函数的对象的类型来执行不同的函数；
 * 形成多态必须具备三个条件：
 * 1、必须存在继承关系；
 * 2、继承关系必须有同名虚函数（其中虚函数是在基类中使用关键字Virtual声明的函数，在派生类中重新定义基类中定义的虚函数时，会告诉编译器不要静态链接到该函数）；
 * 3、存在基类类型的指针或者引用，通过该指针或引用调用虚函数；
*/
#elif __mark == 3

class Shape
{
protected:
  int width;
  int height;

public:
  Shape(int a, int b) : width(a), height(b)
  {
  }
  virtual void printArea()
  {
    std::cout << "parent class area : " << 0 << std::endl;
  }
};

class Rectangle : public Shape
{
public:
  // 调用Shape的构造函数
  Rectangle(int a, int b) : Shape(a, b) {}

public:
  void printArea()
  {
    std::cout << "Rectangle class area : " << (width * height) << std::endl;
  }
};

class Triangle : public Shape
{
public:
  Triangle(int a, int b) : Shape(a, b)
  {
  }
  void printArea()
  {
    std::cout << "Triangle class area : " << (double)(width * height) / 2 << std::endl;
  }
};

int main(void)
{
  Shape *ptr;

  Rectangle r1(3, 4);
  Triangle t1(4, 5);
  // r1.printArea();
  // t1.printArea();
  ptr = &r1;
  ptr->printArea();

  ptr = &t1;
  ptr->printArea();
  
  Shape &ref1 = r1;
  ref1.printArea();
  Shape &ref2 = t1;
  ref2.printArea();

  printf("-----------------end--------------\n");
  return 0;
}
#endif
